#pragma once

#include "room_manage.hpp"
#include "online.hpp"
#include "session.hpp"
#include "matcher.hpp"
#include "db.hpp"


class goabng_server
{
private:

    // 静态资源请求
    void file_handler(webserver_t::connection_ptr& conn)
    {
        auto req =  conn->get_request();
        std::string uri = req.get_uri();

        std::string resource_path = _root_path + uri;

        // 获取资源为根目录
        if (resource_path.back() == '/')
        {
            resource_path += "login.html";
        }

        std::string body;
        if (!Util::file::read(&body, resource_path))
        {
            // 资源请求失败
            LOG(WARNING, "资源路径错误");
            resource_path = _root_path + "/404.html";
            if (!Util::file::read(&body, resource_path))
            {
                LOG(WARNING, "404资源路径错误");
            }
            // 设置status 404
            conn->set_status(websocketpp::http::status_code::not_found);
        }
        else
        {
            conn->set_status(websocketpp::http::status_code::ok);
        }

        conn->append_header("Content-Length", std::to_string(body.size()));
        conn->set_body(body);
    }

    // 注册资源功能请求获取
    void setup(webserver_t::connection_ptr conn)
    {
        Json::Value setup_info;
        
        auto body = conn->get_request_body();
        
        if (!Util::json::deserialize(&setup_info, body))
        {
            LOG(DEBUG, "资源请求失败");
            http_resp(conn, websocketpp::http::status_code::ok, false, "资源请求失败");
            return;
        }

        if (setup_info["username"].isNull() || setup_info["password"].isNull())
        {
            LOG(DEBUG, "用户名密码为空");
            http_resp(conn, websocketpp::http::status_code::ok, false, "用户密码为空");
            return;
        }

        if (!_user_tb->insert(setup_info))
        {
            LOG(NORMAL, "数据库插入失败");
            http_resp(conn, websocketpp::http::status_code::ok, false, "用户名已经被占用");
            return;
        }

        LOG(NORMAL, "注册成功");
        http_resp(conn, websocketpp::http::status_code::ok, true, "用户注册成功");
    }
    
    void http_resp(
        webserver_t::connection_ptr& conn, 
        websocketpp::http::status_code::value code, 
        bool result, 
        const std::string& reason = ""
    )
    {
        Json::Value resp_json;
        if (!result)
        {
            resp_json["result"] = false;
            resp_json["reason"] = reason;
        }
        else 
        {
            resp_json["result"] = true;
        }
        std::string resp_body;
        Util::json::serialize(resp_json, &resp_body);
        
        conn->append_header("Content-Type", "application/json");
        conn->append_header("Content-Length", std::to_string(resp_body.size()));
        conn->set_status(code);
        conn->set_body(resp_body);
    }

    // 登录资源获取
    void login(webserver_t::connection_ptr conn)
    {
        // 1. 获取请求正文
        std::string body = conn->get_request_body();
        Json::Value login_info;
        if (!Util::json::deserialize(&login_info, body))
        {
            LOG(DEBUG, "资源请求失败");
            http_resp(conn, websocketpp::http::status_code::bad_request, false, "资源请求失败");
            return;
        }
        // 2. 验证完整性，进行数据库用户信息验证
        if (login_info["username"].isNull() || login_info["password"].isNull())
        {
            LOG(DEBUG, "用户名密码为空");
            http_resp(conn, websocketpp::http::status_code::bad_request, false, "用户密码为空");
            return;
        }
        if (!_user_tb->login(login_info))
        {
            LOG(NORMAL, "登录失败，请检查用户密码是否正确");
            http_resp(conn, websocketpp::http::status_code::bad_request, false, "登录失败，请检查用户密码是否正确");
            return;
        }
        Json::Value player_records;
        if (!_user_tb->select_by_name(login_info["username"].asString(), player_records))
        {
            LOG(NORMAL, "登录失败，用户不存在");
            http_resp(conn, websocketpp::http::status_code::ok, false, "登录失败，用户不存在");
            return;
        }
        // 3. 失败返回400
        //    成功，给用户创建session
        uint32_t uid = login_info["user_id"].asUInt();
        LOG(DEBUG, "uid: %d", uid);
        session_ptr ssp = _session_manage.create_session(uid);
        if (ssp == nullptr)
        {
            LOG(WARNING, "创建会话失败");
            http_resp(conn, websocketpp::http::status_code::internal_server_error, false, "创建会话失败");
            return;
        }
        
        _session_manage.set_session_expire_time(ssp->ssid());

        // 4. 设置响应报头
        std::string cookie_ssid = "SSID=" + std::to_string(ssp->ssid());
        conn->append_header("Set-Cookie", cookie_ssid);
        http_resp(conn, websocketpp::http::status_code::ok, true, "用户登录成功");
    }

    std::string get_session_from_cookie(const std::string& cookie_str, const std::string& cookie_name)
    {
        //返回SSID
        std::vector<std::string> cookies = Util::string::split(cookie_str, "; ");
        for (const auto& cookie : cookies)
        {
            std::vector<std::string> cookie_ = Util::string::split(cookie, "=");
            if (cookie_.size() != 2)
                continue;
            if (cookie_[0] == cookie_name)
                return cookie_[1];
        }
        return "";
    }

    // 用户信息获取
    void info(webserver_t::connection_ptr conn)
    {
        // 1. 获取cookie信息，从cookie中获取ssid
        std::string cookie = conn->get_request_header("Cookie");
        if (cookie.empty())
        {
            // 返回错误信息
            http_resp(conn, websocketpp::http::status_code::bad_request, false, "cookie未保存");
            return ;
        }
        // 从cookie中找 SSID
        LOG(DEBUG, cookie);
        std::string ssid_str = get_session_from_cookie(cookie, "SSID");
        LOG(DEBUG, "SSID=%s", ssid_str.c_str());
        if (ssid_str == "")
        {
            // 没找到，返回错误信息
            http_resp(conn, websocketpp::http::status_code::bad_request, false, "cookie未找到session, 请重新登录保存cookie");
            return ;
        }

        session_ptr ssp = _session_manage.get_session_from_ssid(std::stoi(ssid_str));
        if (ssp == nullptr)
        {
            // 没找到session, 需要重新登录
            http_resp(conn, websocketpp::http::status_code::bad_request, false, "登录过期, 请重新登录");
            return ;
        }

        uint32_t uid = ssp->get_user();

        LOG(DEBUG, "uid: %d", uid);

        Json::Value user_info;
        if (!_user_tb->select_by_id(uid, user_info))
        {
            // 没有找到用户信息
            http_resp(conn, websocketpp::http::status_code::bad_request, false, "找不到用户信息, 请重新登录");
            return ;
        }

        // 序列化发送数据
        std::string send_body;
        Util::json::serialize(user_info, &send_body);
        conn->append_header("Content-Type", "application/json");
        conn->append_header("Content-Length", std::to_string(send_body.size()));
        conn->set_status(websocketpp::http::status_code::ok);
        conn->set_body(send_body);

        // 刷新session过期时间
        _session_manage.set_session_expire_time(ssp->ssid(), session_manager::SESSION_FOREVER); 

    }

    void http_callback(websocketpp::connection_hdl hdl)
    {
        webserver_t::connection_ptr conn = _wsserver.get_con_from_hdl(hdl);
        auto req =  conn->get_request();
        std::string uri = req.get_uri();
        std::string method = req.get_method();

        LOG(DEBUG, "method: %s", method.c_str());
        LOG(DEBUG, "uri: %s", uri.c_str());

        if ((method == "POST") && (uri == "/setup"))
        {
            setup(conn);
            return;
        }
        else if ((method == "POST") && (uri == "/login"))
        {
            login(conn);
            return;
        }
        else if ((method == "GET") && (uri == "/info"))
        {
            info(conn);
            return;
        }
        file_handler(conn);
    }

    void ws_resp(
        webserver_t::connection_ptr& conn, 
        const std::string& optype,
        bool result, 
        const std::string& reason = ""
    )
    {
        Json::Value resp_json;
        if (!result)
        {
            resp_json["optype"] = optype;
            resp_json["result"] = false;
            resp_json["reason"] = reason;
        }
        else 
        {
            resp_json["optype"] = optype;
            resp_json["result"] = true;
        }
        std::string resp_body;
        Util::json::serialize(resp_json, &resp_body);
        conn->send(resp_body);
    }

    session_ptr check_login(webserver_t::connection_ptr conn)
    {
        std::string cookie_str = conn->get_request_header("Cookie");
        if (cookie_str.empty())
        {
            ws_resp(conn, "hall_ready", false, "未找到Cookie信息, 请重新登录");
            return nullptr;
        }

        std::string ssid_str = get_session_from_cookie(cookie_str, "SSID");
        if (ssid_str == "")
        {
            ws_resp(conn, "hall_ready", false, "Cookie信息未设置, 请重新登录");
            return nullptr;
        }

        session_ptr ssp = _session_manage.get_session_from_ssid(std::stoul(ssid_str));
        if (ssp == nullptr)
        {
            ws_resp(conn, "hall_ready", false, "session已过期, 请重新登录");
            return nullptr;
        }

        return ssp;
    }

    void open_game_hall(webserver_t::connection_ptr conn)
    {
        // 1. 验证是否登录
        session_ptr ssp = check_login(conn);
        if (ssp == nullptr)
            return;

        // 2. 验证是否是重复登录 （不支持顶号）
        if (_online_manage->in_game_hall(ssp->get_user()) || _online_manage->in_game_room(ssp->get_user()))
        {
            ws_resp(conn, "hall_ready", false, "玩家重复登录");
            return;
        }

        // 3. 将当前客户端以及连接放入游戏大厅
        _online_manage->enter_game_hall(ssp->get_user(), conn);

        // 4. 给客户端响应游戏大厅连接成功建立
        ws_resp(conn, "hall_ready", true);

        // 5. 将session设置成永久存在
        _session_manage.set_session_expire_time(ssp->ssid(), session_manager::SESSION_FOREVER);

    }

    void open_game_room(webserver_t::connection_ptr conn)
    {
        // 1. 验证是否登录
        session_ptr ssp = check_login(conn);
        if (ssp == nullptr)
            return;

        // 2. 验证是否是重复登录 （不支持顶号）
        if (_online_manage->in_game_hall(ssp->get_user()) || _online_manage->in_game_room(ssp->get_user()))
        {
            ws_resp(conn, "room_ready", false, "玩家重复登录");
            return;
        }

        // 3. 判断是否已经建立好房间
        room_ptr room = _room_manage->get_room_from_uid(ssp->get_user());
        if (room == nullptr)
        {
            ws_resp(conn, "room_ready", false, "房间获取失败");
            return;
        }

        LOG(DEBUG, "用户：%d 已加入房间", ssp->get_user());
        // 4. 将用户放入到游戏房间中
        _online_manage->enter_game_room(ssp->get_user(), conn);

        // 5. 将session设置为永久存在
        _session_manage.set_session_expire_time(ssp->ssid(), session_manager::SESSION_FOREVER);


        // 6. 成功响应
        Json::Value resp_json = room->RespondSuccess(ssp->get_user());

        std::string resp_body;
        Util::json::serialize(resp_json, &resp_body);
        conn->send(resp_body);
    }

    void open_callback(websocketpp::connection_hdl hdl)
    {
        webserver_t::connection_ptr conn = _wsserver.get_con_from_hdl(hdl);
        auto req =  conn->get_request();
        std::string uri = req.get_uri();

        LOG(DEBUG, "uri: %s", uri.c_str());
        if (uri == "/hall")
        {
            // 建立游戏大厅长连接
            open_game_hall(conn);
            return;
        }
        else if (uri == "/room")
        {
            // 建立游戏房间长连接
            open_game_room(conn);
            return ;
        }
    }

    void close_game_hall(webserver_t::connection_ptr conn)
    {
        // 1. 验证是否登录
        session_ptr ssp = check_login(conn);
        if (ssp == nullptr)
            return;

        
        // 2. 将玩家从游戏大厅移除
        _online_manage->exit_game_hall(ssp->get_user());

        // 3. 将session设置成定时销毁
        _session_manage.set_session_expire_time(ssp->get_user());
    }

    void close_game_room(webserver_t::connection_ptr conn)
    {
        // 1. 验证是否登录
        session_ptr ssp = check_login(conn);
        if (ssp == nullptr)
        {
            LOG(DEBUG, "没有找到房间信息");
            return;
        }

        // 2. 将玩家退出房间
        _online_manage->exit_game_room(ssp->get_user());

        // 3. 将session设置成定时销毁
        _session_manage.set_session_expire_time(ssp->get_user());

        // 4. 将玩家从游戏房间移除
        _room_manage->erase_user_from_room(ssp->get_user());

        LOG(NORMAL, "玩家；%d 已退出", ssp->get_user());
    }

    void close_callback(websocketpp::connection_hdl hdl)
    {
        webserver_t::connection_ptr conn = _wsserver.get_con_from_hdl(hdl);
        auto req =  conn->get_request();
        std::string uri = req.get_uri();

        if (uri == "/hall")
        {
            // 关闭游戏大厅长连接
            close_game_hall(conn);
            return;
        }
        else if (uri == "/room")
        {
            // 关闭游戏房间长连接
            close_game_room(conn);
            return;
        }
    }

    void message_from_hall(webserver_t::connection_ptr conn, webserver_t::message_ptr msg)
    {
        // 1. 验证是否登录
        session_ptr ssp = check_login(conn);
        if (ssp == nullptr)
            return;

        // 2. 获取请求信息
        Json::Value resp_json;
        std::string req_body = msg->get_payload();
        if (!Util::json::deserialize(&resp_json, req_body))
        {
            ws_resp(conn, "unknow", false, "序列化格式错误");
            return;
        }

        // 3. 对请求进行处理 （开始匹配/停止匹配）
        if (!resp_json["optype"].isNull())
        {
            auto op = resp_json["optype"].asString();
            // 开始匹配
            if (op == "match_start")
            {
                if (_match.addMatch(ssp->get_user()))
                {
                    ws_resp(conn, "match_start", true);
                    return;
                }
            }
            // 停止匹配
            else if(op == "match_stop")
            {
                if (_match.delMatch(ssp->get_user()))
                {
                    ws_resp(conn, "match_stop", true);
                    return;
                }
            }
            else if (op == "train_start")
            {
                // 训练房创建成功
                if (_room_manage->create_train_romm(ssp->get_user()) != nullptr)
                {
                    ws_resp(conn, "train_success", true);
                    return;
                }
            }
        }

        ws_resp(conn, "unknow", false, "操作不明确");
    }

    void message_from_room(webserver_t::connection_ptr conn, webserver_t::message_ptr msg)
    {
        // 获取session
        session_ptr ssp = check_login(conn);
        if (ssp == nullptr)
        {
            LOG(DEBUG, "session获取失败");
            return;
        }
        
        // 获取房间信息
        room_ptr room = _room_manage->get_room_from_uid(ssp->get_user());
        if (room == nullptr)
        {
            ws_resp(conn, "unknow", false, "房间获取失败");
            LOG(DEBUG, "房间获取失败");
            return;
        }

        // 序列化消息
        Json::Value req_json;
        std::string req_body = msg->get_payload();
        if (!Util::json::deserialize(&req_json, req_body))
        {
            ws_resp(conn, "unknow", false, "消息请求解析失败");
            LOG(DEBUG, "消息请求解析失败");
            return;
        }
        
        // 让游戏服务端处理
        room->handleRequest(req_json);
    }

    void message_callback(websocketpp::connection_hdl hdl, webserver_t::message_ptr msg)
    {
        webserver_t::connection_ptr conn = _wsserver.get_con_from_hdl(hdl);
        auto req =  conn->get_request();
        std::string uri = req.get_uri();

        if (uri == "/hall")
        {
            message_from_hall(conn, msg);
            return;
        }
        else if (uri == "/room")
        {
            message_from_room(conn, msg);
            return;
        }
    }

public:

    goabng_server(std::string root_path, users_table_ptr ut, online_manager_ptr online, std::shared_ptr<room_manage> room_manage)
        : _root_path(root_path)
        , _user_tb(ut)
        , _online_manage(online)
        , _room_manage(room_manage)
        , _session_manage(&_wsserver)
        , _match(_room_manage, _user_tb, _online_manage)
    {
        _wsserver.set_access_channels(websocketpp::log::alevel::none);
    //设置asio调度器
        _wsserver.init_asio();
        _wsserver.set_reuse_addr(true);

        //设置回调函数
        _wsserver.set_open_handler(std::bind(&goabng_server::open_callback, this, std::placeholders::_1));
        _wsserver.set_http_handler(std::bind(&goabng_server::http_callback, this, std::placeholders::_1));
        _wsserver.set_message_handler(std::bind(&goabng_server::message_callback, this, std::placeholders::_1, std::placeholders::_2));
        _wsserver.set_close_handler(std::bind(&goabng_server::close_callback, this, std::placeholders::_1));
    }

    void start(int port)
    {
        //设置监听端口
        _wsserver.listen(port);
        // 开始接收连接
        _wsserver.start_accept();
        // 服务器启动
        _wsserver.run();
    }

private:
    std::string _root_path;
    users_table_ptr _user_tb;
    online_manager_ptr _online_manage;
    std::shared_ptr<room_manage> _room_manage;
    session_manager _session_manage;
    webserver_t _wsserver;
    mather _match;
};